using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.ComponentModel.Design;
using DMTT.Workflow.Activities;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing.Design;
using DMTT.Workflow.Activities.Design;

namespace DMTT.Workflow.Activities
{
    [ActivityDesignerTheme(typeof(MapperActivityDesignerTheme))]
	public class MapperActivityDesigner : ActivityDesigner
	{
        protected override void OnActivityChanged(ActivityChangedEventArgs e)
        {
            // If the types change recreate the object properties to reflect the new types
            if (e.Member.Name == MapperConstants.SourceType || e.Member.Name == MapperConstants.DestinationType)
                TypeDescriptor.Refresh(e.Activity);
            else
                base.OnActivityChanged(e);
        }

        // Add teh SourceObject and DestinationObject extender properties to the property grid
        protected override void PreFilterProperties(System.Collections.IDictionary properties)
        {
            base.PreFilterProperties(properties);

            MapperActivity ma = this.Activity as MapperActivity;
            if (ma != null)
            {
                properties[MapperConstants.SourceObject] = new ParameterBindingPropertyDescriptor<MapperActivity>(MapperConstants.SourceObject, ma.SourceType,
                                    new Attribute[] { new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), 
                                                      new BrowsableAttribute(true), 
                                                      new CategoryAttribute(MapperConstants.BehaviorCategory),
                                                      new DescriptionAttribute(MapperConstants.SourceObjectDescription),
                                                      new EditorAttribute(typeof(BindUITypeEditor), 
                                                      typeof(UITypeEditor)) });

                properties[MapperConstants.DestinationObject] = new ParameterBindingPropertyDescriptor<MapperActivity>(MapperConstants.DestinationObject, ma.DestinationType,
                        new Attribute[] { new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), 
                                                      new BrowsableAttribute(true), 
                                                      new CategoryAttribute(MapperConstants.BehaviorCategory), 
                                                      new DescriptionAttribute(MapperConstants.DestinationObjectDescription),
                                                      new EditorAttribute(typeof(BindUITypeEditor), 
                                                      typeof(UITypeEditor)) });
            }
        }

        // Add the Edit Mappings context menu item
        protected override ActivityDesignerVerbCollection Verbs
        {
            get
            {
                ActivityDesignerVerbCollection verbCollection = new ActivityDesignerVerbCollection();
                verbCollection.Add(new ActivityDesignerVerb(this, DesignerVerbGroup.Actions, MapperConstants.EditMappingsContextMenuItemText, OnEditMappings));

                return verbCollection;
            }
        }

        // When the context menu item is used we have to shopw the editor
        void OnEditMappings(object sender, EventArgs e)
        {
            // We have to cruft up a context as one isn't available from the design time
            MappingTypeDescriptorContext ctx = new MappingTypeDescriptorContext(this.Activity);

            // Show the mapping editor
            MappingEditor editor = new MappingEditor();
            editor.EditValue(ctx, null, null);
        }
	}

    // Minimal TypeDescriptorContext implementation to cache the activity instance
    // the MappingEditor doesn;t use anything apart from this currently
    class MappingTypeDescriptorContext : ITypeDescriptorContext
    {
        #region ITypeDescriptorContext Members

        public IContainer Container
        {
            get { throw new NotImplementedException(); }
        }

        Activity activity;
        public MappingTypeDescriptorContext(Activity activity)
        {
            this.activity = activity;
        }

        public object Instance
        {
            get { return activity; }
        }

        public void OnComponentChanged()
        {
            throw new NotImplementedException();
        }

        public bool OnComponentChanging()
        {
            throw new NotImplementedException();
        }

        public PropertyDescriptor PropertyDescriptor
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    // PropertyDescriptor implementation to be able to get and set the extender property value effectively
    // This is borrowed from the implmentation from Jon Flanders for his pre-silver WF/WCF integration project
    public class ParameterBindingPropertyDescriptor<T> : PropertyDescriptor
    {
        Type _type = null;

        public ParameterBindingPropertyDescriptor(string propertyName, Type parameterType, Attribute[] attrs)
            : base(propertyName, attrs)
        {
            this._type = parameterType;
        }


        public override TypeConverter Converter
        {
            get
            {
                return new ActivityBindTypeConverter();
            }
        }
        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override Type ComponentType
        {
            get
            {
                return typeof(T);
            }
        }

        public override object GetValue(object component)
        {
            MapperActivity ma = (MapperActivity)component;
            WorkflowParameterBindingCollection parameters = ma.Parameters;
            if (parameters != null && parameters.Contains(this.Name))
            {
                if (parameters[this.Name].IsBindingSet(WorkflowParameterBinding.ValueProperty))
                    return parameters[this.Name].GetBinding(WorkflowParameterBinding.ValueProperty);
                else
                    return parameters[this.Name].GetValue(WorkflowParameterBinding.ValueProperty);
            }
            return null;
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type PropertyType
        {
            get
            {
                if (_type != null)
                    return _type;
                else
                    return typeof(ActivityBind);
            }
        }

        public override void ResetValue(object component)
        {

        }

        public override void SetValue(object component, object value)
        {
            if (component != null)
            {
                ISite site = GetSite(component);
                IComponentChangeService changeService = null;
                if (site != null)
                    changeService = (IComponentChangeService)site.GetService(typeof(IComponentChangeService));
                // Raise the OnComponentChanging event
                changeService.OnComponentChanging(component, this);

                // Save the old value
                object oldValue = GetValue(component);

                try
                {
                    WorkflowParameterBindingCollection parameters = ((MapperActivity)component).Parameters;
                    if (parameters != null)
                    {
                        if (value == null)
                            // Remove the binding from the ParameterBindings collection
                            parameters.Remove(this.Name);
                        else
                        {
                            // Add the binding to the ParameterBindings collection
                            WorkflowParameterBinding binding = null;
                            if (parameters.Contains(this.Name))
                                binding = parameters[this.Name];
                            else
                            {
                                binding = new WorkflowParameterBinding(this.Name);
                                parameters.Add(binding);
                            }

                            // Set the binding value on the ParameterBindings collection correspondent binding item
                            if (value is ActivityBind)
                                binding.SetBinding(WorkflowParameterBinding.ValueProperty, value as ActivityBind);
                            else
                                binding.SetValue(WorkflowParameterBinding.ValueProperty, value);
                        }
                    }
                    // Raise the OnValueChanged event
                    OnValueChanged(component, EventArgs.Empty);
                }
                catch (Exception)
                {
                    value = oldValue;
                    throw;
                }
                finally
                {
                    if (changeService != null)
                        // Raise the OnComponentChanged event
                        changeService.OnComponentChanged(component, this, oldValue, value);
                }
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            return true;
        }
    }
}
